package net.zoneland.knowledge.controller;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;

import net.zoneland.knowledge.model.*;
import net.zoneland.knowledge.service.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.google.common.eventbus.EventBus;

import net.zoneland.knowledge.constant.Constants;
import net.zoneland.knowledge.constant.ResponseMessage;
import net.zoneland.knowledge.constant.WebConstant;
import net.zoneland.knowledge.controller.util.QaQueryUtils;
import net.zoneland.knowledge.enums.QueryTypeEnum;
import net.zoneland.knowledge.enums.VisitTypeEnum;
import net.zoneland.knowledge.service.impl.search.SearchDto;
import net.zoneland.knowledge.utils.LoginUtils;

/**
 * .
 *
 * @author: bcc
 * 2021/5/25
 */
@RestController()
@RequestMapping("/qa")
public class QaQueryController {

    /**
     * logger.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(QaQueryController.class);

    @Autowired
    private QaQueryService qaQueryService;

    @Autowired
    @Qualifier("qaRecordEventBus")
    private EventBus qaRecordEventBus;

    @Autowired
    private QaSatisfactionService qaSatisfactionService;

    @Autowired
    private QaRecordsV2Service qaRecordsV2Service;

    @Autowired
    private DictItemService dictItemService;

    @Autowired
    private UserService userService;

    @Autowired
    private FavoriteService favoriteService;

    @Value("${search.querySubjectFormConfig:true}")
    private boolean querySubjectFormConfig;

    /**
     * 角色业务接口.
     */
    @Autowired
    private RoleService roleService;
    /**
     * 党员党组织业务接口.
     */
    @Autowired
    private PartyService partyService;

    /**
     * 缓存用户角色.
     */
    private Cache<String, List<String>> roleCache = CacheBuilder.newBuilder()
        .expireAfterWrite(2L, TimeUnit.HOURS).build();

    /**
     * 根据关键字进行查询.
     * @param request http请求
     * @param qaQueryDto 查询参数对象
     * @return 查询结果
     * @throws Exception 异常
     */
    @PostMapping("/search")
    public ResponseModel search(final HttpServletRequest request, @RequestBody QaQueryDto qaQueryDto) throws Exception {
        LOGGER.info("search {}", qaQueryDto);
        ResponseModel responseModel = new ResponseModel();
        //验证分类
        if (CollectionUtils.isEmpty(qaQueryDto.getCategoryList())) {
            responseModel.setResult(WebConstant.RESULT_FAIL);
            responseModel.setMessage(ResponseMessage.REQUEST_PARAM_ERROR);
            return responseModel;
        }
        //验证查询关键字
        if (!qaQueryDto.isShowAll() && StringUtils.isEmpty(qaQueryDto.getQueryText())) {
            responseModel.setResult(WebConstant.RESULT_FAIL);
            responseModel.setMessage(ResponseMessage.REQUEST_PARAM_ERROR);
        } else {
            qaQueryDto.setQaType(QueryTypeEnum.SEARCH);
            dealQaQueryDto(request, qaQueryDto);
            dealSatisfaction(qaQueryDto.getTalkSessionId(), qaQueryDto.getLoginUserPojo());
            SearchDto searchDto = qaQueryService.search(qaQueryDto);
            responseModel.setData(searchDto.getQaResponse());
            responseModel.setAnswerId(searchDto.getAnswerId());
            qaRecordEventBus.post(searchDto);
        }
        return responseModel;
    }

    /**
     * 根据关键字进行查询.
     * @param request http请求
     * @param qaQueryDto 查询参数对象
     * @return 查询结果
     * @throws Exception 异常
     */
    @PostMapping("/search4ds")
    public ResponseModel searchForDataService(final HttpServletRequest request,
                                              final @RequestBody QaQueryDto qaQueryDto) throws Exception {
        LOGGER.info("search {}", qaQueryDto);
        ResponseModel responseModel = new ResponseModel();
        //验证分类
        if (CollectionUtils.isEmpty(qaQueryDto.getCategoryList())) {
            responseModel.setResult(WebConstant.RESULT_FAIL);
            responseModel.setMessage(ResponseMessage.REQUEST_PARAM_ERROR);
            return responseModel;
        }
        //验证查询关键字
        if (!qaQueryDto.isShowAll() && StringUtils.isEmpty(qaQueryDto.getQueryText())) {
            responseModel.setResult(WebConstant.RESULT_FAIL);
            responseModel.setMessage(ResponseMessage.REQUEST_PARAM_ERROR);
        } else {
            qaQueryDto.setQaType(QueryTypeEnum.SEARCH);
            dealQaQueryDtoForDataService(request, qaQueryDto);
            SearchDto searchDto = qaQueryService.search(qaQueryDto);
            responseModel.setData(searchDto.getQaResponse());
            responseModel.setAnswerId(searchDto.getAnswerId());
        }
        return responseModel;
    }

    /**
     * 根据levelTitle进行查询.
     * @param request http请求
     * @param qaQueryDto 查询参数对象
     * @return 查询结果
     * @throws Exception 异常
     */
    @PostMapping("/queryByPath")
    public ResponseModel queryByPath(final HttpServletRequest request, @RequestBody QaQueryDto qaQueryDto) throws Exception {
        LOGGER.info("queryByPath {}", qaQueryDto);
        ResponseModel responseModel = new ResponseModel();
        //验证分类
        if (StringUtils.isBlank(qaQueryDto.getCategory())) {
            responseModel.setResult(WebConstant.RESULT_FAIL);
            responseModel.setMessage(ResponseMessage.REQUEST_PARAM_ERROR);
            return responseModel;
        }
        //验证查询关键字
        if (StringUtils.isEmpty(qaQueryDto.getLevel1Title())) {
            responseModel.setResult(WebConstant.RESULT_FAIL);
            responseModel.setMessage(ResponseMessage.REQUEST_PARAM_ERROR);
        } else {
            dealQaQueryDto(request, qaQueryDto);
            dealSatisfaction(qaQueryDto.getTalkSessionId(), qaQueryDto.getLoginUserPojo());
            qaQueryDto.setQaType(QueryTypeEnum.QUERY_BY_PATH);
            SearchDto searchDto = null;
            if(querySubjectFormConfig && StringUtils.isEmpty(qaQueryDto.getLevel3Title())) {
                 searchDto = qaQueryService.queryByPathCombineConfig(qaQueryDto);
            } else {
                 searchDto = qaQueryService.queryByPath(qaQueryDto);
            }
            responseModel.setData(searchDto.getQaResponse());
            responseModel.setAnswerId(searchDto.getAnswerId());
            qaRecordEventBus.post(searchDto);
        }
        return responseModel;
    }

    /**
     * 根据levelTitle进行查询.
     * @param request http请求
     * @param qaQueryDto 查询参数对象
     * @return 查询结果
     * @throws Exception 异常
     */
    @PostMapping("/querybypath4ds")
    public ResponseModel queryByPathForDataService(final HttpServletRequest request, @RequestBody QaQueryDto qaQueryDto) throws Exception {
        LOGGER.debug("queryByPathForDataService {}", qaQueryDto);
        final ResponseModel responseModel = new ResponseModel();

        //验证分类和关键字
        if (StringUtils.isBlank(qaQueryDto.getCategory()) || StringUtils.isEmpty(qaQueryDto.getLevel1Title())) {
            responseModel.setResult(WebConstant.RESULT_FAIL);
            responseModel.setMessage(ResponseMessage.REQUEST_PARAM_ERROR);
            return responseModel;
        }

        //执行查询
        dealQaQueryDtoForDataService(request, qaQueryDto);
        qaQueryDto.setQaType(QueryTypeEnum.QUERY_BY_PATH);
        final SearchDto searchDto;
        if (querySubjectFormConfig && StringUtils.isEmpty(qaQueryDto.getLevel3Title())) {
             searchDto = qaQueryService.queryByPathCombineConfig(qaQueryDto);
        } else {
             searchDto = qaQueryService.queryByPath(qaQueryDto);
        }
        responseModel.setData(searchDto.getQaResponse());
        responseModel.setAnswerId(searchDto.getAnswerId());
        return responseModel;
    }

    /**
     * 根据EsId进行查询.
     * @param request http请求
     * @param qaQueryDto 查询参数对象
     * @return 查询结果
     * @throws Exception 异常
     */
    @PostMapping("/queryById")
    public ResponseModel queryById(final HttpServletRequest request, @RequestBody QaQueryDto qaQueryDto) throws Exception {
        LOGGER.info("queryById {}", qaQueryDto);
        ResponseModel responseModel = new ResponseModel();
        final LoginUserPojo user = LoginUtils.getCurrentUserByToken(request.getHeader(Constants.TOKEN_NAME));
        //验证查询关键字
        if (StringUtils.isEmpty(qaQueryDto.getId())) {
            responseModel.setResult(WebConstant.RESULT_FAIL);
            responseModel.setMessage(ResponseMessage.REQUEST_PARAM_ERROR);
            return responseModel;
        }
        qaQueryDto.setQaType(QueryTypeEnum.QUERY_BY_ID);
        dealQaQueryDto(request, qaQueryDto);
        dealSatisfaction(qaQueryDto.getTalkSessionId(), qaQueryDto.getLoginUserPojo());
        SearchDto searchDto = qaQueryService.queryById(qaQueryDto);
        responseModel.setAnswerId(searchDto.getAnswerId());
        checkFavoriteStatus(searchDto.getQaResponse(), user);
        responseModel.setData(searchDto.getQaResponse());
        qaRecordEventBus.post(searchDto);
        return responseModel;
    }
    /**
     * 根据EsId进行查询.
     * @param request http请求
     * @param qaQueryDto 查询参数对象
     * @return 查询结果
     * @throws Exception 异常
     */
    @PostMapping("/querybyid4ds")
    public ResponseModel queryByIdForDataService(final HttpServletRequest request,
                                                 final @RequestBody QaQueryDto qaQueryDto) throws Exception {
        LOGGER.info("queryByIdForDataService {}", qaQueryDto);
        ResponseModel responseModel = new ResponseModel();
        //验证查询关键字
        if (StringUtils.isEmpty(qaQueryDto.getId())) {
            responseModel.setResult(WebConstant.RESULT_FAIL);
            responseModel.setMessage(ResponseMessage.REQUEST_PARAM_ERROR);
            return responseModel;
        }
        qaQueryDto.setQaType(QueryTypeEnum.QUERY_BY_ID);
        dealQaQueryDtoForDataService(request, qaQueryDto);
        SearchDto searchDto = qaQueryService.queryById(qaQueryDto);
        responseModel.setAnswerId(searchDto.getAnswerId());
        responseModel.setData(searchDto.getQaResponse());
        return responseModel;
    }

    /**
     * 处理请求参数.
     * @param request http请求
     * @param qaQueryDto 查询参数对象
     */
    private void dealQaQueryDto(final HttpServletRequest request, @RequestBody QaQueryDto qaQueryDto)
        throws Exception {
        //获取当前登录人信息
        final String token = request.getHeader(Constants.TOKEN_NAME);
        LoginUserPojo user = LoginUtils.getCurrentUserByToken(token);
        qaQueryDto.setLoginUserPojo(user);
        qaQueryDto.setFlag(user.getCompanyDN());
        //前端回车发送消息存在多了回车转义符的情况，后端也过滤下
        if (StringUtils.isNotBlank(qaQueryDto.getQueryText())) {
            qaQueryDto.setQueryText(qaQueryDto.getQueryText().replaceAll("\n",""));
        }
        qaQueryDto.setOrgDeptDnList(getUserDept(user));
        qaQueryDto.setUserIds(Lists.newArrayList(user.getUserID()));
        qaQueryDto.setRoleFlags(LoginUtils.getCurrentUserRoleByToken(token));
        if (user.getGanShi()) {
            final List<String> roles = Optional.of(qaQueryDto.getRoleFlags()).orElse(Collections.EMPTY_LIST);
            roles.add(Constants.ROLE_GANSHI);
            qaQueryDto.setRoleFlags(roles);
        }
        String visitType = request.getHeader("visit_type");
        if (StringUtils.isNotBlank(visitType)) {
            qaQueryDto.setVisitType(VisitTypeEnum.getByValue(visitType));
        } else {
            qaQueryDto.setVisitType(VisitTypeEnum.MOA);
        }
    }

    /**
     * 处理请求参数.
     * @param request http请求
     * @param qaQueryDto 查询参数对象
     */
    private void dealQaQueryDtoForDataService(final HttpServletRequest request, @RequestBody QaQueryDto qaQueryDto)
        throws Exception {
        //获取当前登录人信息
        final LoginUserPojo userTmp = qaQueryDto.getLoginUserPojo();
        //此处需要重新初始化当前用户的登录信息，业务接口只传了userId信息
        final LoginUserPojo user = QaQueryUtils.getLoginUserPojoByUserId(userTmp.getUserID(), userService, partyService);
        qaQueryDto.setLoginUserPojo(user);

        //根据人员信息初始化查询相关信息
        qaQueryDto.setFlag(user.getCompanyDN());
        //前端回车发送消息存在多了回车转义符的情况，后端也过滤下
        if (StringUtils.isNotBlank(qaQueryDto.getQueryText())) {
            qaQueryDto.setQueryText(qaQueryDto.getQueryText().replaceAll("\n",""));
        }
        //业务接口只提供PC查询方式
        qaQueryDto.setVisitType(VisitTypeEnum.PC);
        qaQueryDto.setOrgDeptDnList(getUserDept(user));
        qaQueryDto.setUserIds(Lists.newArrayList(user.getUserID()));
        qaQueryDto.setRoleFlags(getUserRole(user.getUserID()));
        if (user.getGanShi()) {
            //如果是干事，设置相应角色
            final List<String> roles = Stream.of(
                    CollectionUtils.emptyIfNull(qaQueryDto.getRoleFlags()).stream(),
                    Stream.of(Constants.ROLE_GANSHI)
                )
                .flatMap(Function.identity())
                .collect(Collectors.toList());
            qaQueryDto.setRoleFlags(roles);
        }
    }

    /**
     * 获取人员角色集合.
     * @param userId 人员uid
     * @return 人员角色集合
     * @throws ExecutionException
     */
    private List<String> getUserRole(final String userId) throws ExecutionException {
        return roleCache.get(userId, () ->
            Optional.ofNullable(userId)
                .map(roleService::getByUserId)
                .map(it ->
                    it.stream().map(Role::getRoleId).collect(Collectors.toList())
                )
            .orElseGet(Collections::emptyList));
    }

    /**
     * 第一次会话时保存.
     * @param talkSessionId sessionId
     * @param user 用户
     */
    private void dealSatisfaction(final String talkSessionId, final LoginUserPojo user) {
        try {
            List<QaRecordsV2> list = qaRecordsV2Service.getFirstSessionId(user.getUserID(), talkSessionId);
            if (CollectionUtils.isNotEmpty(list)) {
                return;
            }
            QaSatisfaction qaSatisfaction = new QaSatisfaction();
            qaSatisfaction.setId(UUID.randomUUID().toString());
            //其他基础信息
            qaSatisfaction.setSessionId(talkSessionId);
            String reqId = Optional.ofNullable(user.getUserIdCard()).filter(StringUtils::isNotBlank).orElse(user.getUserID());
            qaSatisfaction.setReqId(reqId);
            qaSatisfaction.setReqName(user.getUserName());
            qaSatisfaction.setCmpDn(user.getCompanyDN());
            qaSatisfaction.setDeptDn(user.getDepartmentDN());
            //默认满意
            qaSatisfaction.setStatus(1);
            qaSatisfaction.setCreateTime(new Date());
            qaSatisfactionService.save(qaSatisfaction);
        } catch (Exception e) {
            LOGGER.error("默认满意度保存失败", e);
        }
    }

    private List<String> getUserDept(final LoginUserPojo user) throws Exception {
        List<String> deptDnList = new ArrayList<>();
        final List<DictItem> dictItemList = dictItemService.findDictByParentIdAndDictName("partDept", user.getUserID());
        for (DictItem dictItem : dictItemList) {
            deptDnList.add(dictItem.getDictValue());
        }
        deptDnList.addAll(userService.getUserDeptCodeAndParentDeptCode(user));
//        String[] deptArr = user.getDepartmentDN().split(",");
//        StringBuffer deptDn = new StringBuffer("");
//        if (deptArr.length > 1) {
//            deptDn.append(deptArr[deptArr.length - 2]).append(",").append(deptArr[deptArr.length - 1]);
//            if (org.apache.commons.lang.StringUtils.isNotBlank(CmpDnEnum.getCityByValue(deptDn.toString()))) {
//                deptDn = new StringBuffer("");
//                deptDn.append(deptArr[deptArr.length - 3]).append(",").append(deptArr[deptArr.length - 2])
//                    .append(",").append(deptArr[deptArr.length - 1]);
//            }
//        } else {
//            deptDn.append(deptArr[deptArr.length - 1]);
//        }
//        deptDnList.add(deptDn.toString());
//        deptDnList.add(user.getDepartmentDN());
        return deptDnList;
    }

    /**
     * 判断并且设置内容的收藏状态
     * @param qaResponse qaResponse
     * @param user user
     */
    private void checkFavoriteStatus(final QaResponse qaResponse, final LoginUserPojo user){
        qaResponse.setFavoriteStatus(favoriteService.getFavoriteStatus(qaResponse, user));
    }

}
